/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - core/threads
// Copyright (c) 2014.  All Rights Reserved
//
// File:		AEThread.h
// Author:		Gianluca Belardelli
// Date:		02/01/2014
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AETHREADLOCALDATA_H_
#define _AETHREADLOCALDATA_H_

#if defined(AE_LINUX) || defined( AE_PLATFORM_ANDROID )

	#define USE_COMPILER_TLS
	#define AE_THREAD_LOCAL(TYPE) __thread TYPE
	#define AE_THREAD_LOCAL_SET(var,value) var = value
	#define AE_THREAD_LOCAL_GET(var) var

#elif defined(WIN32)

	extern "C"
	{
		__declspec(dllimport) unsigned long _stdcall TlsAlloc(void);
		__declspec(dllimport) void* _stdcall TlsGetValue( unsigned long dwTlsIndex );		
		__declspec(dllimport) int _stdcall TlsSetValue( unsigned long dwTlsIndex, void* lpTlsValue);
		__declspec(dllimport) int _stdcall TlsFree( unsigned long dwTlsIndex );
	}

	template<typename T>
	class AEThreadLocalData
	{
		// Members
		public:
			AEUINT32 m_uiSlotID; 

		// Methods
		public:
			AEThreadLocalData( void ) 
			{ 
				m_uiSlotID = TlsAlloc();
				AE_ONLYONDEBUG( if ( m_uiSlotID == 0xFFFFFFFF ) { AEDBGBREAK; } ) 
			}

			~AEThreadLocalData( void ) 
			{ 
				AE_ONLYONDEBUG( int nRes = ) TlsFree( m_uiSlotID );
				AE_ONLYONDEBUG( if ( nRes == 0 ) { AEDBGBREAK; } ) 
			}

			AE_FORCEINLINE T GetData( void ) const
			{
				return (T)(AEUINT32)TlsGetValue( m_uiSlotID ) ;
			}

			AE_FORCEINLINE void SetData(T p) 
			{
				AEUINT32 v = AEUINT32(p);
				AE_ONLYONDEBUG( int nRes = ) TlsSetValue( m_uiSlotID, (void*)v );
				AE_ONLYONDEBUG( if ( nRes == 0 ) { AEDBGBREAK; } )
			}
	};

	#define AE_THREAD_LOCAL(TYPE) AEThreadLocalData<TYPE>
	#define AE_THREAD_LOCAL_SET(var,value) var.SetData(value)
	#define AE_THREAD_LOCAL_GET(var) var.GetData()

#elif defined(AE_MACOSX86) || defined(AE_IOS)

	#include <pthread.h>

	template<typename T>
	class AEThreadLocalData
	{
		// Members
		public:
			pthread_key_t m_ptKey; 

		// Methods
		public:
			AEThreadLocalData( void ) 
			{ 
				pthread_key_create( &m_ptKey, 0L );
			}

			~AEThreadLocalData( void ) 
			{ 
				pthread_key_delete( m_ptKey );
			}

			AE_FORCEINLINE T GetData( void ) const
			{
				return (T) pthread_getspecific( m_ptKey );
			}

			AE_FORCEINLINE void SetData(T p) 
			{
				pthread_setspecific( m_ptKey, (void*)p );
			}
	};

	#define AE_THREAD_LOCAL(TYPE) AEThreadLocalData<TYPE>
	#define AE_THREAD_LOCAL_SET(var,value) var.SetData(value)
	#define AE_THREAD_LOCAL_GET(var) var.GetData()

#else
#error "AEThreadLocalData.h --> Nessuna piattaforma specificata!"
#endif

#endif // _AETHREADLOCALDATA_H_

